//
//  OverlayView.m
//  operator
//
//  Created by Andrej on 21.12.11.
//  Copyright (c) 2011 RWTH Aachen. All rights reserved.
//

#import "OverlayView.h"

@interface OverlayView(PrivateMethods)
    -(NSPoint) getLocationInView:(NSPoint)loc;
@end

@implementation OverlayView

-(id) initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
    }
    return self;
}

-(void) dealloc {
    [bindingAnnotationId1 release];
    [bindingAnnotationId2 release];
    [super dealloc];
}

-(void) drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    /* Flip screen coordinate manually. */
    NSRect frameRect = [self bounds];
    NSAffineTransform* xform = [NSAffineTransform transform]; 
    [xform translateXBy:0.0 yBy:frameRect.size.height]; 
    [xform scaleXBy:1.0 yBy:-1.0]; 
    [xform concat];

    /* Draw existing bindings for own team members with a dashed line.
     To this end, iterate over all existing map annotations and check
     if a binding exists. */
    for( id annotationId in [[GameState defaultState] mapAnnotations] ) {
        MapIcon *annotation = (MapIcon*)[[[GameState defaultState] mapAnnotations] objectForKey:annotationId];
        if( (annotation.type == MapIconTypeOwnHunter || annotation.type == MapIconTypeOwnRunner)
            && [controller isAnnotationIdValid:[annotation bindingId]] ) {
            NSBezierPath *path = [NSBezierPath bezierPath];

            NSPoint p1 = [controller getAnnotationLocation:annotation.ID],
                    p2 = [controller getAnnotationLocation:annotation.bindingId];

            [path setLineWidth: 1.5];
            [path setLineCapStyle:NSRoundLineCapStyle];
            CGFloat dashArray[2] = {2.0, 3.0};
            [path setLineDash:dashArray count:2 phase:0.0];
            [[NSColor blackColor] set];

            [path moveToPoint:p1];
            [path lineToPoint:p2];

            [path stroke];

            NSBezierPath *circlePath = [NSBezierPath bezierPath];
            [circlePath setLineDash:dashArray count:2 phase:0.0];
            [circlePath appendBezierPathWithOvalInRect: NSMakeRect(p1.x-14, p1.y-14, 28, 28)];
            [circlePath stroke];

            [circlePath appendBezierPathWithOvalInRect: NSMakeRect(p2.x-14, p2.y-14, 28, 28)];
            [circlePath stroke];
        }
    }

    /* Draw "rubber band" from a source annotation the the mouse position */
    if( [controller isAnnotationIdValid:bindingAnnotationId1] ) {
        //NSLog(@"Drawing rubber band from %@ to (%f, %f)", bindingAnnotationId1, mousePosition.x, mousePosition.y);
        NSBezierPath *path = [NSBezierPath bezierPath];

        [path setLineWidth: 2.0];
        [path setLineCapStyle:NSRoundLineCapStyle];

        [path moveToPoint:[controller getAnnotationLocation:bindingAnnotationId1]];
        
        /* Check if it it already a valid binding. */
        if( [controller isValidBindingBetween:bindingAnnotationId1 And:bindingAnnotationId2] ) {
            /* draw line in blue color */
            [[NSColor blueColor] set];
            [path lineToPoint:[controller getAnnotationLocation:bindingAnnotationId2]];

            /* draw two large circles at start and end */
            NSBezierPath *circlePath = [NSBezierPath bezierPath];
            NSPoint p = [controller getAnnotationLocation:bindingAnnotationId1];
            [circlePath appendBezierPathWithOvalInRect: NSMakeRect(p.x-14, p.y-14, 28, 28)];
            [circlePath stroke];

            p = [controller getAnnotationLocation:bindingAnnotationId2];
            [circlePath appendBezierPathWithOvalInRect: NSMakeRect(p.x-14, p.y-14, 28, 28)];
            [circlePath stroke];
        } else {
            /* draw line in black color */
            [[NSColor blackColor] set];
            [path lineToPoint:mousePosition];

            /* draw two small filled circles at start and end */
            NSBezierPath *circlePath = [NSBezierPath bezierPath];
            NSPoint p = [controller getAnnotationLocation:bindingAnnotationId1];
            [circlePath appendBezierPathWithOvalInRect: NSMakeRect(p.x-3, p.y-3, 6, 6)];
            [circlePath fill];

            [circlePath appendBezierPathWithOvalInRect: NSMakeRect(mousePosition.x-3, mousePosition.y-3, 6, 6)];
            [circlePath fill];
        }
        [path stroke];
    } else { /* if the "source" annotation does not exists (anymore) */
        /* release the binding state */
        if( bindingAnnotationId1 != nil ) {
            NSLog(@"Binding aborted. Annotation does not exists anymore.");
            [bindingAnnotationId1 release];
            bindingAnnotationId1 = nil;
        }
        if( bindingAnnotationId2 != nil ) {
            [bindingAnnotationId2 release];
            bindingAnnotationId2 = nil;
        }
    }
}

#pragma Event Handlers

-(void) mouseDown:(NSEvent *)theEvent{
    mousePosition = [self getLocationInView:[theEvent locationInWindow]];
    NSLog(@"mouseDownEvent at (%f, %f)", mousePosition.x, mousePosition.y);

    /* Get the annotation key or nil for the position under the mouse. */
    bindingAnnotationId1 = [controller getAnnotationLocatedAt:mousePosition];
    if( bindingAnnotationId1 != nil ) {
        NSLog(@"Annotation %@ found at this position.", bindingAnnotationId1);
        /* Remember the mouse down position in order to indicate a simple click. */
        downMousePosition = mousePosition;
        NSLog(@"bindingProcessStarted");
    }
    [super mouseDown:theEvent];

    [self setNeedsDisplay:true];
}

-(void) mouseDragged:(NSEvent *)theEvent{
    mousePosition = [self getLocationInView:[theEvent locationInWindow]];

    /* Check if binding is in progress.  */
    if( bindingAnnotationId1 != nil ) {
        /* Get the annotation key or nil for the position under the mouse.
           Using this we already can check for valid bindings while still
           dragging the mouse, and thus, do some highlighting and snap in. */
        [bindingAnnotationId2 release];
        bindingAnnotationId2 = [controller getAnnotationLocatedAt:mousePosition];
        NSLog(@"Annotation %@ found at this position.", bindingAnnotationId2);
    } else {
        [super mouseDragged:theEvent];
    }

    [self setNeedsDisplay:true];
}

-(void) mouseUp:(NSEvent *)theEvent{
    mousePosition = [self getLocationInView:[theEvent locationInWindow]];
    NSLog(@"mouseUpEvent at (%f, %f)", mousePosition.x, mousePosition.y);

    /* Check if binding is in progress.  */
    if( bindingAnnotationId1 != nil ) {
        /* Check if it was ust a simple mouse click */
        if( mousePosition.x == downMousePosition.x && mousePosition.y == downMousePosition.y ) {
            NSLog(@"Just a simple click... :(");
            [bindingAnnotationId1 release];
            bindingAnnotationId1 = nil;
            [bindingAnnotationId2 release];
            bindingAnnotationId2 = nil;
        } else if( bindingAnnotationId2 == nil ) { /* Check if we have a "target" annotation. */
            NSLog(@"Nothing here...");
            /* Then release the current binding for the "source" annotation. */
            if( [controller unsetBindingOf:bindingAnnotationId1] ) {
                NSLog(@"Released binding");
            }
            [bindingAnnotationId1 release];
            bindingAnnotationId1 = nil;
        } else { /* We have a valid binding! */
            /* Send it to the server via the controller... */
            if( [controller setBindingFrom:bindingAnnotationId1 To:bindingAnnotationId2] ) {
                NSLog(@"Valid binding. Set binding from %@ to %@.", bindingAnnotationId1, bindingAnnotationId2);
            } else {
                NSLog(@"Binding from %@ to %@ is invalid", bindingAnnotationId1, bindingAnnotationId2);
            }
            /* binding is over :(. clear everything */
            [bindingAnnotationId1 release];
            bindingAnnotationId1 = nil;
            [bindingAnnotationId2 release];
            bindingAnnotationId2 = nil;
            NSLog(@"bindingProcessEnded");
        }
    }
    [super mouseUp:theEvent];

    [self setNeedsDisplay:true];
}

#pragma Private Methods

/*
 * This method helps us to translate the given point from a
 * coordinate system with the origin in the bottom left corner
 * to one with the origin in the upper left corner.
 */
-(NSPoint) getLocationInView:(NSPoint)loc {
    NSPoint retValue = [self convertPoint:loc fromView:nil];
    NSAffineTransform* xform = [NSAffineTransform transform]; 
    [xform translateXBy:0.0 yBy:[self bounds].size.height]; 
    [xform scaleXBy:1.0 yBy:-1.0];
    retValue = [xform transformPoint:retValue];
    return retValue;
}

@end
